﻿using System;
using System.Collections.Generic;
using DataService.Enums;
using DataService.Structs;
using DataService.Tag;

namespace DataService;

public delegate void AlarmEventHandler(object sender, AlarmItem e);

public interface IEvent
{
    bool IsEnabled { get; set; }
    bool IsAcked { get; set; }
    bool IsActived { get; set; }
    Severity Severity { get; }
    EventType EventType { get; }
    DateTime LastActive { get; set; }
    string Comment { get; }
}

public sealed class LevelAlarm : SimpleCondition
{
    public override AlarmType AlarmType
    {
        get { return AlarmType.Level; }
    }

    public LevelAlarm(int id, string source, string comment, float deadBand = 0f, int delay = 0) :
        base(id, ConditionType.Absolute, source, comment, 0, deadBand, delay)
    {
        _subConditions = new SubCondition[4]
            {
                 new SubCondition(SubAlarmType.HiHi),
                 new SubCondition(SubAlarmType.High),
                 new SubCondition(SubAlarmType.LoLo),
                 new SubCondition(SubAlarmType.Low)
            };
    }

    protected override void CheckAlarm(object sender, ValueChangingEventArgs<Storage> e)
    {
        if (_enable)
        {
            SubCondition condition;
            float value = _tag.ScaleToValue(e.NewValue);
            for (int i = 0; i < _subConditions.Length; i++)
            {
                if (_subConditions[i].IsEnabled)
                {
                    condition = _subConditions[i];
                    if (i < 2)//Hi Alarm
                    {
                        if (value > condition.Threshold)
                        {
                            OnActive(condition, e.NewValue, e.NewTimeStamp);
                            return;
                        }
                        else if (_deadBand > 0 && ActiveSubCondition == condition.SubAlarmType && value > condition.Threshold - _deadBand)
                        {
                            return;
                        }
                    }
                    else//Low Alarm
                    {
                        if (value < condition.Threshold)
                        {
                            OnActive(condition, e.NewValue, e.NewTimeStamp);
                            return;
                        }
                        else if (_deadBand > 0 && ActiveSubCondition == condition.SubAlarmType && value > condition.Threshold + _deadBand)
                        {
                            return;
                        }
                    }
                }
            }
            OnInActive(e.NewValue);
        }
    }

    public override bool AddSubCondition(SubCondition condition)
    {
        switch (condition.SubAlarmType)
        {
            case SubAlarmType.HiHi:
                _subConditions[0] = condition;
                return true;
            case SubAlarmType.High:
                _subConditions[1] = condition;
                return true;
            case SubAlarmType.LoLo:
                _subConditions[2] = condition;
                return true;
            case SubAlarmType.Low:
                _subConditions[3] = condition;
                return true;
            default:
                return false;
        }
    }

    public override bool RemoveSubCondition(SubCondition condition)
    {
        switch (condition.SubAlarmType)
        {
            case SubAlarmType.HiHi:
                _subConditions[0] = SubCondition.Empty;
                return true;
            case SubAlarmType.High:
                _subConditions[1] = SubCondition.Empty;
                return true;
            case SubAlarmType.LoLo:
                _subConditions[2] = SubCondition.Empty;
                return true;
            case SubAlarmType.Low:
                _subConditions[3] = SubCondition.Empty;
                return true;
            default:
                return false;
        }
    }
}

public sealed class DevAlarm : SimpleCondition
{
    public override AlarmType AlarmType
    {
        get { return AlarmType.Dev; }
    }

    public DevAlarm(int id, ConditionType conditionType, string source, string comment, float para, float deadBand = 0f, int delay = 0) :
        base(id, conditionType, source, comment, para, deadBand, delay)
    {
        _subConditions = new SubCondition[2]
            {
                new SubCondition(SubAlarmType.MajDev),
                new SubCondition(SubAlarmType.MinDev)
            };
    }

    protected override void CheckAlarm(object sender, ValueChangingEventArgs<Storage> e)
    {
        if (_enable)
        {
            float value = _tag.ScaleToValue(e.NewValue) - _para;
            SubCondition condition;
            for (int i = 0; i < _subConditions.Length; i++)
            {
                if (_subConditions[i].IsEnabled)
                {
                    condition = _subConditions[i];
                    if (value > (_conditionType == ConditionType.Absolute ? condition.Threshold : _para * condition.Threshold))
                    {
                        OnActive(condition, e.NewValue, e.NewTimeStamp);
                        return;
                    }
                    else if (_deadBand > 0 && ActiveSubCondition == condition.SubAlarmType &&
                        ((_conditionType == ConditionType.Absolute && value > condition.Threshold - _deadBand)
                            || (_conditionType == ConditionType.Percent && value > _para * (condition.Threshold - _deadBand))))
                    {
                        return;
                    }
                }
            }
            OnInActive(e.NewValue);
        }
    }

    public override bool AddSubCondition(SubCondition condition)
    {
        switch (condition.SubAlarmType)
        {
            case SubAlarmType.MajDev:
                _subConditions[0] = condition;
                return true;
            case SubAlarmType.MinDev:
                _subConditions[1] = condition;
                return true;
            default:
                return false;
        }
    }

    public override bool RemoveSubCondition(SubCondition condition)
    {
        switch (condition.SubAlarmType)
        {
            case SubAlarmType.MajDev:
                _subConditions[0] = SubCondition.Empty;
                return true;
            case SubAlarmType.MinDev:
                _subConditions[1] = SubCondition.Empty;
                return true;
            default:
                return false;
        }
    }
}

public sealed class DigitAlarm : SimpleCondition
{
    public override AlarmType AlarmType
    {
        get { return AlarmType.Dsc; }
    }

    public DigitAlarm(int id, string source = null, string comment = null, int delay = 0) :
        base(id, ConditionType.Absolute, source, comment, 0, 0f, delay)
    {
        _subConditions = new SubCondition[1]
            {
                new SubCondition(SubAlarmType.Dsc)
            };
    }

    protected override void CheckAlarm(object sender, ValueChangingEventArgs<Storage> e)
    {
        if (_enable)
        {
            SubCondition condition = _subConditions[0];
            if (condition.IsEnabled)
            {
                if (e.NewValue.Boolean == condition.Threshold > 0)
                {
                    OnActive(condition, e.NewValue, e.NewTimeStamp);
                    return;
                }
            }
            OnInActive(e.NewValue);
        }
    }

    public override bool AddSubCondition(SubCondition condition)
    {
        switch (condition.SubAlarmType)
        {
            case SubAlarmType.Dsc:
                _subConditions[0] = condition;
                return true;
            default:
                return false;
        }
    }

    public override bool RemoveSubCondition(SubCondition condition)
    {
        switch (condition.SubAlarmType)
        {
            case SubAlarmType.Dsc:
                _subConditions[0] = SubCondition.Empty;
                return true;
            default:
                return false;
        }
    }
}

public sealed class WordDigitAlarm : SimpleCondition
{
    public override AlarmType AlarmType
    {
        get { return AlarmType.WordDsc; }
    }

    public WordDigitAlarm(int id, string source = null, string comment = null, int delay = 0) :
        base(id, ConditionType.Absolute, source, comment, 0, 0f, delay)
    {
        _subConditions = new SubCondition[16];
        for (int i = 0; i < 16; i++)
        {
            _subConditions[i].SubAlarmType = SubAlarmType.Dsc;
            _subConditions[i].Threshold = i;
        };
    }

    protected override void CheckAlarm(object sender, ValueChangingEventArgs<Storage> e)
    {
        if (_enable)
        {
            short newvalue = e.NewValue.Int16;
            short oldvlaue = e.OldValue.Int16;
            if (newvalue == 0 && oldvlaue != 0)
            {
                OnInActive(e.NewValue);
                return;
            }
            for (short i = 0; i < _subConditions.Length; i++)
            {
                SubCondition condition = _subConditions[i];
                if (condition.IsEnabled)
                {
                    int mask = 1 << i;
                    int newval = mask & newvalue;
                    int oldval = mask & oldvlaue;
                    if (newval != 0 && oldval == 0)
                    {
                        OnActive(condition, new Storage { Int16 = i }, e.NewTimeStamp);
                    }
                }
            }
        }
    }

    public override bool AddSubCondition(SubCondition condition)
    {
        switch (condition.SubAlarmType)
        {
            case SubAlarmType.Dsc:
                int index = (int)condition.Threshold;
                if (index >= 0 && index < 16)
                    _subConditions[index] = condition;
                return true;
            default:
                return false;
        }
    }

    public override bool RemoveSubCondition(SubCondition condition)
    {
        switch (condition.SubAlarmType)
        {
            case SubAlarmType.Dsc:
                int index = (int)condition.Threshold;
                if (index >= 0 && index < 16)
                    _subConditions[index] = SubCondition.Empty;
                return true;
            default:
                return false;
        }
    }

    protected override void OnActive(SubCondition condition, Storage value, DateTime timeStamp)
    {
        if (ActiveSubCondition == SubAlarmType.None)
        {
            _active = true;
            _condLastActive = timeStamp;
        }
        _ack = false;
        ActiveSubCondition = condition.SubAlarmType;
        _current = new AlarmItem(timeStamp, condition.Message, value.Int16, ActiveSubCondition, condition.Severity, _id, _source);
        if (AlarmActive != null)
        {
            foreach (AlarmEventHandler deleg in AlarmActive.GetInvocationList())
            {
                deleg.BeginInvoke(this, _current, null, null);
            }
        }
        RaiseChanged("Value");
    }

    protected override void OnInActive(Storage value)
    {
        if (ActiveSubCondition != SubAlarmType.None)
        {
            _active = false;
            ActiveSubCondition = SubAlarmType.None;
            _current.Duration = DateTime.Now - LastActive;
            _current = new AlarmItem(DateTime.Now, string.Concat("【", Comment, "】", ALARMSTOP), 0, SubAlarmType.None, Severity.Normal, _id, _source);
            if (AlarmActive != null)
            {
                foreach (AlarmEventHandler deleg in AlarmActive.GetInvocationList())
                {
                    deleg.BeginInvoke(this, _current, null, null);
                }
            }
        }
    }
}

public sealed class QualitiesAlarm : SimpleCondition
{
    public override AlarmType AlarmType
    {
        get { return AlarmType.Quality; }
    }

    public QualitiesAlarm(int id, string source, string comment, int delay = 0) :
        base(id, ConditionType.Absolute, source, comment, 0, 0f, delay)
    {
        _subConditions = new SubCondition[1]
            {
                new SubCondition(SubAlarmType.BadPV)
            };
    }

    protected override void CheckAlarm(object sender, ValueChangingEventArgs<Storage> e)
    {
        if (_enable)
        {
            SubCondition condition = _subConditions[0];
            if (condition.IsEnabled)
            {
                if (e.Quality != QUALITIES.QUALITY_GOOD)
                    OnActive(condition, e.NewValue, e.NewTimeStamp);
                return;
            }
            OnInActive(e.NewValue);
        }
    }

    public override bool AddSubCondition(SubCondition condition)
    {
        switch (condition.SubAlarmType)
        {
            case SubAlarmType.BadPV:
                _subConditions[0] = condition;
                return true;
            default:
                return false;
        }
    }

    public override bool RemoveSubCondition(SubCondition condition)
    {
        switch (condition.SubAlarmType)
        {
            case SubAlarmType.BadPV:
                _subConditions[0] = SubCondition.Empty;
                return true;
            default:
                return false;
        }
    }
}

public sealed class ROCAlarm : SimpleCondition
{
    public override AlarmType AlarmType
    {
        get { return AlarmType.ROC; }
    }


    public ROCAlarm(int id, string souce, string comment, float deadBand = 0f, int delay = 0) :
        base(id, ConditionType.Percent, souce, comment, 0, deadBand, delay)
    {
        _subConditions = new SubCondition[2]
            {
                new SubCondition(SubAlarmType.MajROC),
                new SubCondition(SubAlarmType.MinROC)
            };
    }

    protected override void CheckAlarm(object sender, ValueChangingEventArgs<Storage> e)
    {
        if (_enable)
        {
            float value = (float)((_tag.ScaleToValue(e.NewValue) - e.OldValue.Single) / (e.NewTimeStamp - e.OldTimeStamp).TotalMilliseconds);
            SubCondition condition;
            for (int i = 0; i < _subConditions.Length; i++)
            {
                if (_subConditions[i].IsEnabled)
                {
                    condition = _subConditions[i];
                    if (value > condition.Threshold)
                    {
                        OnActive(condition, e.NewValue, e.NewTimeStamp);
                        return;
                    }
                    else if (_deadBand > 0 && ActiveSubCondition == condition.SubAlarmType && value > condition.Threshold - _deadBand)
                    {
                        return;
                    }
                }
            }
            OnInActive(e.NewValue);
        }
    }

    public override bool AddSubCondition(SubCondition condition)
    {
        switch (condition.SubAlarmType)
        {
            case SubAlarmType.MajROC:
                _subConditions[0] = condition;
                return true;
            case SubAlarmType.MinROC:
                _subConditions[1] = condition;
                return true;
            default:
                return false;
        }
    }

    public override bool RemoveSubCondition(SubCondition condition)
    {
        switch (condition.SubAlarmType)
        {
            case SubAlarmType.MajROC:
                _subConditions[0] = SubCondition.Empty;
                return true;
            case SubAlarmType.MinROC:
                _subConditions[1] = SubCondition.Empty;
                return true;
            default:
                return false;
        }
    }
}

public struct SubCondition
{
    public bool IsEnabled;

    public SubAlarmType SubAlarmType;

    public Severity Severity;

    public float Threshold;

    public string Message;

    public SubCondition(SubAlarmType type, float threshold = 0f, Severity severity = Severity.Normal, string message = "", bool enabled = true)
    {
        this.SubAlarmType = type;
        this.Threshold = threshold;
        this.Severity = severity;
        this.Message = message;
        this.IsEnabled = enabled;
    }

    public static readonly SubCondition Empty = new SubCondition(SubAlarmType.None, 0f, Severity.Normal, "正常", false);
}

public class CompareCondBySource : IComparer<ConditionBase>
{
    public int Compare(ConditionBase x, ConditionBase y)
    {
        if (x == null || x.Source == null)
        {
            return y == null || y.Source == null ? 0 : 1;
        }
        else
        {
            return y == null || y.Source == null ? 1 : x.Source.CompareTo(y.Source);
        }
    }
}
